package com.radar.task;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.google.gson.Gson;
import com.radar.algorithm.MathCommon;
import com.radar.algorithm.Storms;
import com.radar.common.*;
import com.radar.exception.RadarCaclError;
import com.radar.exception.RadarDecodeError;
import com.radar.io.CinradRadar;
import com.radar.io.RadarGridData;
import com.radar.utils.RefObj;
import com.radar.visualize.PolarMap;
import com.radar.visualize.RasterMap;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.meteoinfo.global.Extent;
import org.meteoinfo.global.PointD;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import ucar.ma2.InvalidRangeException;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class ZZTask extends BaskTask {

    @Value("${radar.root-zz-path:''}")
    public void setRootPath(String rootPath) {
        super.rootPath = rootPath;
    }

    @Value("${radar.process-zz-path:''}")
    public void setProcessPath(String processPath) {
        super.processPath = processPath;
    }

    @Value("${radar.handle-hour:-1}")
    public void setHandleHour(int handleHour) {
        super.handleHour = handleHour;
    }

    @Override
    @Scheduled(cron = "0 0/6 * * * ? ")
    public void jobRun() {
        if (!super.filterDir()) {
            return;
        }
        log("start 721 radar task poll date : " + DateUtil.formatDateTime(DateUtil.date()), "RADAR-SINGLE-ZZ");
        long time = System.currentTimeMillis();
        FileUtil.mkdir(processPath);
        FileUtil.loopFiles(this.rootPath, new RegexFileFilter("^(\\d{14})0.05V$"))
                .stream()
                .filter(i -> {
                    if (this.handleHour == -1) {
                        return true;
                    } else {
                        // handle hour
                        Date date = DateUtil.parse(i.getName().substring(0, 12), DateConstants.DATE_FORMAT_YYYYMMDDHHMM);
                        return date.getTime() > DateUtil.offsetHour(new Date(), -handleHour).getTime();
                    }
                }).forEach(this::process);
        log("end 721 radar task poll times: " + (System.currentTimeMillis() - time), "RADAR-SINGLE-ZZ");
    }

    @Override
    public void process(File file) {
        File OKFile = new File(super.okPath + File.separator + "RADAR-SINGLE-ZZ" + File.separator + file.getName() + ".OK");
        if (OKFile.exists()) {
            return;
        } else {
            FileUtil.touch(OKFile);
        }
        log("start parse 721 radar file : " + file.getAbsolutePath(), "RADAR-SINGLE-ZZ");
        DateTime parse = DateUtil.parse(file.getName().substring(0, 14), DatePattern.PURE_DATETIME_PATTERN);
        String relativePath = this.processPath + File.separator + DateUtil.format(parse, DateConstants.DATE_FORMAT_YYYYMMDD) + File.separator + DateUtil.format(parse, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + File.separator;
        FileUtil.mkdir(relativePath);
        // cp file
        File target = new File(relativePath + file.getName());
        try {
            FileUtil.writeBytes(FileUtil.readBytes(file), target);
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            long time = System.currentTimeMillis();
            CinradRadar cinradRadar = new CinradRadar();
            log("start parse radar file " + target.getName(), "RADAR-SINGLE-ZZ");
            cinradRadar.parse(target.getAbsolutePath(), CinradType.RADAR_721);
            Date date = cinradRadar.radar.dateTime;
            RadarSite.RadarSiteItem radarSite = cinradRadar.radar.getRadarSite();
            Map<String, Object> result = new HashMap<>();
            result.put("code", cinradRadar.code);
            result.put("alt", cinradRadar.alt);
            result.put("radarSite", radarSite);
            result.put("date", DateUtil.format(cinradRadar.radar.dateTime, DateConstants.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS));
            result.put("type", cinradRadar.type.name());
            result.put("dataType", cinradRadar.datType.name());
            result.put("vcp", cinradRadar.radar.vcp);
            result.put("elevation", cinradRadar.radar.getElevation());
            result.put("rresolution", cinradRadar.radar.getResolution(ProductType.REF));
            result.put("vresolution", cinradRadar.radar.getResolution(ProductType.VEL));
            // 写入基本信息
            FileUtil.writeUtf8String(new Gson().toJson(result), relativePath + "setting.json");
            // 画图
            int[] refIndex = cinradRadar.radar.getElevationIndex(ProductType.REF);
            int[] velIndex = cinradRadar.radar.getElevationIndex(ProductType.VEL);
            BufferedImage bufferedImage;

            log("开始处理 单层 ppi station: " + radarSite.getStation() + ",date : " + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ",path : " + target.getAbsolutePath(), "RADAR-SINGLE-ZZ");
            ImageIO.setUseCache(false);
            PolarMap polarMap = new PolarMap(cinradRadar.radar);
            RasterMap rasterMap = new RasterMap(cinradRadar.radar);
            for (int i = 0; i < refIndex.length; i++) {
                bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.REF).backColor(new Color(0, 0, 0, 0)), refIndex[i]);
                ImageIO.write(bufferedImage, "png", new File(relativePath + "REF_" + i + ".png"));

                bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.VEL).backColor(new Color(0, 0, 0, 0)), velIndex[i]);
                ImageIO.write(bufferedImage, "png", new File(relativePath + "VEL_" + i + ".png"));
            }
            // draw cr
            log("开始处理 et ppi station: " + radarSite.getStation() + ",date : " + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ",path : " + target.getAbsolutePath(), "RADAR-SINGLE-ZZ");

            RadarGridData et = cinradRadar.radar.getET(0);
            bufferedImage = rasterMap.simpleRaster(RasterMap.buildRasterInfo(ProductType.ET).backColor(new Color(0, 0, 0, 0)), et);
            ImageIO.write(bufferedImage, "png", new File(relativePath + "ET.png"));

            log("开始处理 vil ppi station: " + radarSite.getStation() + ",date : " + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ",path : " + target.getAbsolutePath(), "RADAR-SINGLE-ZZ");
            RadarGridData vil = cinradRadar.radar.getVIL(0);
            bufferedImage = rasterMap.simpleRaster(RasterMap.buildRasterInfo(ProductType.VIL).backColor(new Color(0, 0, 0, 0)), vil);
            ImageIO.write(bufferedImage, "png", new File(relativePath + "VIL.png"));

            log("开始处理 ppi_ref ppi station: " + radarSite.getStation() + ",date : " + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ",path : " + target.getAbsolutePath(), "RADAR-SINGLE-ZZ");
            bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.REF).backColor(new Color(0, 0, 0, 0)), cinradRadar.radar.getElevationIndex(ProductType.REF)[0]);
            ImageIO.write(bufferedImage, "png", new File(relativePath + "PPI_REF.png"));

            log("开始处理 ppi_vel ppi station: " + radarSite.getStation() + ",date : " + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ",path : " + target.getAbsolutePath(), "RADAR-SINGLE-ZZ");
            bufferedImage = rasterMap.simplePPI(RasterMap.buildRasterInfo(ProductType.VEL).backColor(new Color(0, 0, 0, 0)), cinradRadar.radar.getElevationIndex(ProductType.VEL)[0]);
            ImageIO.write(bufferedImage, "png", new File(relativePath + "PPI_VEL.png"));

            log("开始处理 cr station: " + radarSite.getStation() + ",date : " + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ",path : " + target.getAbsolutePath(), "RADAR-SINGLE-ZZ");
            bufferedImage = rasterMap.simpleCR(RasterMap.buildRasterInfo(ProductType.REF).backColor(new Color(0, 0, 0, 0)));
            ImageIO.write(bufferedImage, "png", new File(relativePath + "CR.png"));

            // draw end
            Extent extent = getExtent(ProductType.CR, cinradRadar, 0);
            FileUtil.writeUtf8String(extent.minX + ";" + extent.maxX + ";" + extent.minY + ";" + extent.maxY, relativePath + File.separator + "extent.txt");

            // stroms
            RadarGridData cr = cinradRadar.radar.getCR();
            double[][] decode = MathCommon.decode(cr.radarData, ProductType.REF);
            int step = 4;
            double[][] data = new double[decode.length / step][decode[0].length / step];
            for (int i = 0; i < data.length; i++) {
                for (int j = 0; j < data[0].length; j++) {
                    data[i][j] = decode[i * step][j * step];
                }
            }
            RefObj<Integer> count = new RefObj<>(0);
            List<Storms.StormDt> list = Storms.getStorms(data, step, count, 28, cinradRadar);
            list.forEach(z -> {
                z.setCenter(new PointD(cr.xArray[(int) z.getCenter().Y], cr.yArray[cr.yArray.length - 1 - (int) z.getCenter().X]));
                z.getBoundary().forEach(point -> {
                    double x = cr.xArray[(int) point.Y];
                    double y = cr.yArray[cr.yArray.length - 1 - (int) point.X];
                    point.X = x;
                    point.Y = y;
                });
                z.getContain().forEach(point -> {
                    double x = cr.xArray[(int) point.Y];
                    double y = cr.yArray[cr.yArray.length - 1 - (int) point.X];
                    point.X = x;
                    point.Y = y;
                });
            });

            FileUtil.writeUtf8String(new Gson().toJson(list), relativePath + "storms.json");
            // write 3d

            String rPath = relativePath + "surface.dat";

            double[] alt = new double[]{1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000};
            byte[][][] cappi = cinradRadar.radar.cappi(ProductType.REF, alt);
            double jself = 500.0 / cappi[0].length;
            double kself = 500.0 / cappi[0][0].length;
            byte[] bytes = new byte[alt.length * 500 * 500];
            int index = 0;
            for (int i = 0; i < alt.length; i++) {
                for (int j = 0; j < 500; j++) {
                    for (int k = 0; k < 500; k++) {
                        int jj = (int) Math.floor((500 - 1 - j) / jself);
                        int kk = (int) Math.floor(k / kself);
                        if (cappi[i][jj][kk] == -128 || MathCommon.DecodeRef(cappi[i][jj][kk]) <= 5) {
                            bytes[index] = (byte) 255;
                        } else {
                            bytes[index] = (byte) MathCommon.DecodeRef(cappi[i][jj][kk]);
                        }
                        index++;
                    }
                }
            }
            File f = new File(rPath);
            FileUtil.writeBytes(bytes, f);
            ZipUtil.zip(f.getAbsolutePath(), f.getAbsolutePath() + ".zip");
            f.delete();
            cinradRadar.radar.flush();
            log("end parse radar file " + target.getName() + " time : " + (System.currentTimeMillis() - time), "RADAR-SINGLE-ZZ");

        } catch (Exception e) {
            log("error write radar file : " + file.getName() + " e : " + e.getMessage(), "RADAR-SINGLE-ZZ");
        }
    }

    public Extent getExtent(ProductType type, CinradRadar cinradRadar, int index) throws RadarCaclError, RadarDecodeError, IOException, InvalidRangeException {
        Extent extent = null;
        double lon = cinradRadar.radar.getRadarSite().getLon();
        double lat = cinradRadar.radar.getRadarSite().getLat();
        if (type == ProductType.REF || type == ProductType.VEL) {
            double res = cinradRadar.radar.getResolution(type);
            byte[][] raw = cinradRadar.radar.getRaw(index, type);
            double ele = cinradRadar.radar.getElevation()[index];
            double distance = raw[0].length * res;
            distance = Math.cos(ele * Const.DEG2RAD) * distance;
            extent = Common.getExtentRadius(lon, lat, distance);
        } else if (type == ProductType.CR) {
            extent = cinradRadar.radar.getCR().getExtent();
        } else if (type == ProductType.ET) {
            extent = cinradRadar.radar.getET().getExtent();
        } else if (type == ProductType.EB) {
            extent = cinradRadar.radar.getEB().getExtent();
        } else if (type == ProductType.VIL) {
            extent = cinradRadar.radar.getVIL().getExtent();
        }
        return extent;
    }
}
